/**************************************************************/
/*  This file is licensed from, and is a trade secret of:     */
/*                                                            */
/*    Mercury Computer Systems, Incorporated                  */
/*    199 Riverneck Road                                      */
/*    Chelmsford, Massachusetts 01824-2820                    */
/*    United States of America                                */
/*    Telephone + 1 978 256-1300                              */
/*                                                            */
/*  Refer to your Software License Agreement for provisions   */
/*  on use, duplication, or disclosure.                       */
/*  US. Government users and prime contractors are also       */
/*  governed by the following Data Rights Notice:             */
/*  Use, duplication or disclosure by the US. Government is   */
/*  subject to the provisions set forth in FAR 52.227-14 for  */
/*  restricted rights or other agency data rights provisions  */
/*  on commercial software and documentation, as applicable.  */
/*                                                            */
/*  This file: (a) was developed at private expense, and no   */
/*  part was developed with government funds, (b) is a        */
/*  trade secret of Mercury Computer Systems, Inc. for the    */
/*  purposes of the Freedom of Information Act, (c) is        */
/*  "commercial computer software" subject to limited         */
/*  utilization as provided in the above noted Software       */
/*  License Agreement and (d) is in all respects data         */
/*  belonging exclusively to Mercury Computer Systems, Inc..  */
/*                                                            */
/**************************************************************/

/******************************************************************/
/* Copyright (c) 1999-2003, Mercury Computer Systems, Inc.,       */
/* Chelmsford MA.,and all third party embedded software sources.  */
/* All rights reserved under the Copyright laws of US. and        */
/* international treaties.                                        */
/*                                                                */
/* Patent Pending                                                 */
/******************************************************************/


//$COPYRIGHT_END

#include "fm3tr.h"

using namespace std;

extern "C" {
  FM3TR_IMPORT
  POA_CF::Resource *
  CreateFM3TR_WaveformPacketizer (const char *identifier) 
  {
    FM3TR_WaveformPacketizer * temp = 0;

    if (!FM3TR_WaveformPacketizer::Create (temp, identifier)) {
      return 0;
    }

    return temp;
  }

  FM3TR_IMPORT
  POA_CF::Resource *
  CreateFM3TR_WaveformReceiver (const char *identifier) 
  {
    FM3TR_WaveformReceiver * temp = 0;

    if (!FM3TR_WaveformReceiver::Create (temp, identifier)) {
      return 0;
    }

    return temp;
  }
}

/*
 * Constants and static data.
 */

int FM3TR_Constants::Preamble_Phasing_Bits = 2350;
int FM3TR_Constants::Preamble_Guard_Time_Bits = 250;
int FM3TR_Constants::Preamble_Bits_Per_Block = 512;
int FM3TR_Constants::Hops_Per_Frame = 4;
int FM3TR_Constants::Bits_Per_Frame = 320;
int FM3TR_Constants::Symbols_Per_Frame = 500;
int FM3TR_Constants::Nrt = 5;
int FM3TR_Constants::Ninf = 80;
int FM3TR_Constants::Ntt = 10;
int FM3TR_Constants::Ngt = 8;
unsigned long FM3TR_Constants::A_Code = 0x015ACF9Elu;
unsigned long FM3TR_Constants::S_Code = 0x6DEA771Elu;

int FM3TR_WaveformReceiver::m_bitcount[16] = {
  /* 0000 */ -4, /* 0001 */ -2, /* 0010 */ -2, /* 0011 */ 0,
  /* 0100 */ -2, /* 0101 */  0, /* 0110 */  0, /* 0111 */ 2,
  /* 1000 */ -2, /* 1001 */  0, /* 1010 */  0, /* 1011 */ 2,
  /* 1100 */  0, /* 1101 */  2, /* 1110 */  2, /* 1111 */ 4
};

/*
 * ----------------------------------------------------------------------
 * FM3TR Packetizer
 * ----------------------------------------------------------------------
 */

status
FM3TR_WaveformPacketizer::Create (FM3TR_WaveformPacketizer *& res,
				  const char * identifier)
{
  res = new FM3TR_WaveformPacketizer (identifier);

  if (!res) {
    return fail;
  }

  return success;
}

FM3TR_WaveformPacketizer::FM3TR_WaveformPacketizer (const char *identifier)
  : CF__Resource_impl (identifier),
    m_threadrunning (false),
    m_bStop (false),
    m_llc_enable (false),
    m_llc_ctrl_disable (false)
{
  /*
   * Header (32 bits) + 7 code words (@32 bits) + preamble guard time.
   * Does not include preamble phasing pattern.
   */

  m_output_preamble = new char [8*32 + FM3TR_Constants::Preamble_Guard_Time_Bits];

  // Define ports.
  m_cvsdin = new CvsdInPort_impl (2*FM3TR_Constants::Bits_Per_Frame);

  m_output_frame = new char [FM3TR_Constants::Symbols_Per_Frame];

  unsigned long max_data_size = max (FM3TR_Constants::Symbols_Per_Frame, 
				     FM3TR_Constants::Preamble_Bits_Per_Block);
  m_fm3trout = new Fm3trOutPort_impl (m_output_frame, max_data_size);
  m_llc_ctrl_in = new LLC_Ctrl_InPort_impl<FM3TR_WaveformPacketizer> (this);

  /*
   * Initialize some static arrays.
   */

  m_phasing_pattern = new char [FM3TR_Constants::Preamble_Phasing_Bits];
  m_a_code = new char [32];
  m_s_code = new char [32];
  m_inv_s_code = new char [32];

  for (int i=0; i<FM3TR_Constants::Preamble_Phasing_Bits; i++) {
    m_phasing_pattern[i] = (i&1) ? 1 : -1;
  }

  unsigned long bit = 0x80000000;

  for (int i=0; i<32; i++, bit>>=1) {
    m_a_code[i] = (FM3TR_Constants::A_Code & bit) ? 1 : -1;
    m_s_code[i] = (FM3TR_Constants::S_Code & bit) ? 1 : -1;
    m_inv_s_code[i] = (FM3TR_Constants::S_Code & bit) ? -1 : 1;
  }
}

FM3TR_WaveformPacketizer::~FM3TR_WaveformPacketizer ()
{
  delete m_phasing_pattern;
  delete m_a_code;
  delete m_s_code;
  delete m_inv_s_code;
  delete m_output_frame;
  delete m_output_preamble;

  // delete port & intf instances
  delete m_cvsdin;
  delete m_fm3trout;
  delete m_llc_ctrl_in;
}

CORBA::Object_ptr
FM3TR_WaveformPacketizer::getPort (const CORBA::Char* name)
  throw (CORBA::SystemException,
	 CF::PortSupplier::UnknownPort)
{
  string temp(name);

  if (temp == "FM3TR_WaveformPacketizerIn") {
    return m_cvsdin->getPort ();
  }
  else if (temp == "FM3TR_WaveformPacketizerOut") {
    return m_fm3trout->getPort ();
  }
  else if (temp == "LLC_Ctrl_Input") {
    return m_llc_ctrl_in->getPort ();
  }
  else {
    throw CF::PortSupplier::UnknownPort();
  }
} // End of FM3TR_WaveformPacketizer::getPort()

void
FM3TR_WaveformPacketizer::start ()
  throw (CORBA::SystemException,
	 CF::Resource::StartError)
{
  if (m_threadrunning) {
    return;
  }

  m_threadrunning = true;
  m_thread.start (RunWorker, (void *) this);
  CF__Resource_impl::start();
} // End of void FM3TR_WaveformPacketizer::start ()

void
FM3TR_WaveformPacketizer::stop ()
  throw (CORBA::SystemException,
	 CF::Resource::StopError)
{
  m_bStop = true;   
  m_threadrunning = false;
  m_thread.detach ();
  m_cvsdin->ReleaseDataBuffer ();
} // End of void FM3TR_WaveformPacketizer::stop ()

status
FM3TR_WaveformPacketizer::Init ()
{
#ifdef MCS_VERBOSE
  std::cout << "FM3TR_WaveformPacketizer: Initializing." << std::endl;
#endif

  // Read in operational mode from properties

  /*
   * Property UUIDs (from .prf.xml):
   *
   * <simple id="DCE:6a26cad0-bf4d-4006-aabe-01e1181e47d7" type="string" name="Operational_Mode" mode="readwrite">
   *   <kind kindtype="configure" />
   * </simple>
   * <simple id="DCE:ec0146b9-f3fb-4e67-ad08-551466ce5b0f" type="boolean" name="LLC_Control_Disable" mode="readwrite">
   *   <kind kindtype="configure" />
   * </simple>
   */

  const char * prop_operational_mode;
  int index;

  try {
    // Get value of Operational Mode property
    const char * prop_id = "DCE:6a26cad0-bf4d-4006-aabe-01e1181e47d7"; //UUID
    index = GetPropertyIndex (prop_id);

    if (index < 0) {
      return fail;
    }

    if (GetPropertyValue (index, & prop_operational_mode)) {
      return fail;
    }

    if (strcmp (prop_operational_mode, "voice") == 0) {
      m_voice_mode = true;
    }
    else if (strcmp (prop_operational_mode, "data") == 0) {
      m_voice_mode = false;
    }
    else {
      return fail;
    }

    // Get value of LLC_Control_Disable property
    CORBA::Boolean bool_prop;
    prop_id = "DCE:ec0146b9-f3fb-4e67-ad08-551466ce5b0f"; //UUID
    index = GetPropertyIndex (prop_id);

    if (index < 0) {
      return fail;
    }

    if (GetPropertyValue (index, &bool_prop)) {
      return fail;
    }

    if (bool_prop) {
      m_llc_ctrl_disable = true;
    }
    else {
      m_llc_ctrl_disable = false;
    }
  }
  catch ( ... ) {
    return fail;
  }

#ifdef MCS_VERBOSE
  std::cout << "FM3TR_WaveformPacketizer: Initialized; FM3TR mode is: ";

  if (m_voice_mode) {
    std::cout << "voice";
  }
  else {
    std::cout << "data";
  }

  std::cout << "; LLC CTRL is ";

  if (m_llc_ctrl_disable) {
    std::cout << "disabled";
  }
  else {
    std::cout << "enabled";
  }

  std::cout << "." << std::endl;
#endif

  return success;
}

// Generic thread OS signature which calls component specific thread function.
void
FM3TR_WaveformPacketizer::RunWorker (void * param)
{
  FM3TR_WaveformPacketizer * component =
    (FM3TR_WaveformPacketizer *) param;

#ifdef MCS_VERBOSE
  std::cout << "FM3TR_WaveformPacketizer: Component is running." << std::endl;
#endif

  component->RunLoop ();

#ifdef MCS_VERBOSE
  std::cout << "FM3TR_WaveformPacketizer: Component is stopping." << std::endl;
#endif
} // End of FM3TR_WaveformPacketizer::RunWorker (void * param)

status
FM3TR_WaveformPacketizer::RunLoop ()
{
  if (success != Init()) {
    return fail;
  }

  m_bStop = false;    

  /*
   * Run as long as LLC control is allowed and until stop command
   * is encountered.
   */

  while (!m_bStop) {
    if (m_llc_ctrl_disable || m_llc_enable) {
      Run ();
    }
  }

  return success;
} // End of FM3TR_WaveformPacketizer::RunLoop()

// Inner loop method that performs component business logic.
status
FM3TR_WaveformPacketizer::Run ()
{
  bool idle, eom;
  int frame;

  /*
   * Number of frames before "EOM" is sent.  Emulates a "push-to-talk"
   * scenario where the user releases the button every few seconds.  Also
   * tests the receiver's ability to re-acquire periodically, particulary
   * if synchronization is lost.  There are 50 frames per second for real-
   * time operation (at 250 hops/s).
   */

  int frame_reset_count = 2500;

  idle = true;
  eom = false;
  frame = 0;

  while (!eom && !m_bStop) {
    // Waiting for data.  Try to lock buffer with timeout.

    m_input_count = m_cvsdin->GetData (&m_input);

    if (m_input_count == 0) {
      continue;
    }
    else if (m_input_count < 64) {
      // Not enough samples in input.
      m_cvsdin->ReleaseDataBuffer (0);
      continue;
    }

    if (m_voice_mode) {
      m_operational_mode = FM3TR_Constants::VOICE_250_HPS;
    }
    else {
      m_operational_mode = FM3TR_Constants::DATA_250_HPS;   
    }

    m_num_read = 0;

    if (m_input_count == 0 && idle) {
      break;
    }

    /*
     * We're at EOM either when getting an EOD from upstream, or when
     * receiving less than the expected amount of data. Actually, the
     * former should only happen if the latter is also true (but there
     * can be an EOD after receiving a full frame!).
     */

    if ((frame==frame_reset_count-1) && (m_voice_mode)) {
      /*
       * Set eom when frames reaches a certain number.
       * Fill remaining m_input buffer with zeros.
       */

      memset (m_input+m_input_count, 0, FM3TR_Constants::Bits_Per_Frame-m_input_count);
      eom = true;
    }

    if (m_input_count == 0) {
      eom = true;
    }

    if (idle) {
#ifdef MCS_VERBOSE
      std::cout << "FM3TR_WaveformPacketizer: Sending preamble." << std::endl;
#endif
      SendPreamble (m_operational_mode);
      idle = false;
    }

    m_output_count = 0;
    while (m_input_count >= 64) {
      frame++;

#ifdef MCS_VERBOSE
      if (!m_voice_mode) {
	std::cout << "FM3TR_WaveformPacketizer: Encoding frame "
		  << frame << "." << std::endl;
      }
#endif

      EncodeFrame (eom);

      m_fm3trout->pushData (m_output_frame, FM3TR_Constants::Symbols_Per_Frame);
      m_output_count += FM3TR_Constants::Symbols_Per_Frame;
    }

    m_cvsdin->ReleaseDataBuffer(m_num_read);
  } // while (!eom)

  /*
   * End of message.
   */

#ifdef MCS_VERBOSE
  std::cout << "FM3TR_WaveformPacketizer: At end of message after " << frame << " frames." << endl;
#endif
  return success;
}  // End of FM3TR_WaveformPacketizer::Run()


void
FM3TR_WaveformPacketizer::SendPreamble (int mode)
{
#ifdef MCS_VERBOSE
  std::cout << "FM3TR_WaveformPacketizer: Sending preamble." << std::endl;
#endif

  /*
   * Synchronization pattern and phasing pattern; send in pieces rather than
   * a single 2,350-element block in order to keep the buffer size down.
   */

  int bits_per_block = FM3TR_Constants::Preamble_Bits_Per_Block;
  int bits_remaining = FM3TR_Constants::Preamble_Phasing_Bits;
  int bits_to_send;

  for (int i=0; i<FM3TR_Constants::Preamble_Phasing_Bits; i+=bits_per_block) {
    bits_to_send = (bits_remaining < bits_per_block) ? bits_remaining : bits_per_block;
    m_fm3trout->pushData (m_phasing_pattern+i, bits_to_send);
    bits_remaining -= bits_to_send;
  }

  /*
   * Prepare entire preamble in an output buffer and call PushPacket
   * on the entire block.  This ensures that all the necessary data are
   * present in the buffer by the time the semaphore is locked so that
   * the receiver can read it.  The Acquire() method in the Receiver
   * exits if it finds the A-Code but can't read more data.
   */

  char * output_ptr = m_output_preamble;

  // Synchronization A-code
  memcpy (output_ptr, m_a_code, 32);
  output_ptr += 32;

  /*
   * Code words 0-2; determine hope rate (250/500/1k/2k Hz) and mode
   * (data/voice).
   */

  switch (mode) {
  case FM3TR_Constants::DATA_2000_HPS:
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformPacketizer: Data mode, 2000 hops/s." << std::endl;
#endif

    memcpy (output_ptr,    m_s_code, 32);
    memcpy (output_ptr+32, m_s_code, 32);
    memcpy (output_ptr+64, m_s_code, 32);
    output_ptr += 96;
    break;

  case FM3TR_Constants::VOICE_2000_HPS:
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformPacketizer: Voice mode, 2000 hops/s." << std::endl;
#endif

    memcpy (output_ptr,    m_inv_s_code, 32);
    memcpy (output_ptr+32, m_s_code,     32);
    memcpy (output_ptr+64, m_s_code,     32);
    output_ptr += 96;
    break;

  case FM3TR_Constants::DATA_1000_HPS:
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformPacketizer: Data mode, 1000 hops/s." << std::endl;
#endif

    memcpy (output_ptr,    m_s_code,     32);
    memcpy (output_ptr+32, m_inv_s_code, 32);
    memcpy (output_ptr+64, m_s_code,     32);
    output_ptr += 96;
    break;

  case FM3TR_Constants::VOICE_1000_HPS:
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformPacketizer: Voice mode, 1000 hops/s." << std::endl;
#endif

    memcpy (output_ptr,    m_inv_s_code, 32);
    memcpy (output_ptr+32, m_inv_s_code, 32);
    memcpy (output_ptr+64, m_s_code,     32);
    output_ptr += 96;
    break;

  case FM3TR_Constants::DATA_500_HPS:
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformPacketizer: Data mode, 500 hops/s." << std::endl;
#endif

    memcpy (output_ptr,    m_s_code,     32);
    memcpy (output_ptr+32, m_s_code,     32);
    memcpy (output_ptr+64, m_inv_s_code, 32);
    output_ptr += 96;
    break;

  case FM3TR_Constants::VOICE_500_HPS:
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformPacketizer: Voice mode, 500 hops/s." << std::endl;
#endif

    memcpy (output_ptr,    m_inv_s_code, 32);
    memcpy (output_ptr+32, m_s_code,     32);
    memcpy (output_ptr+64, m_inv_s_code, 32);
    output_ptr += 96;
    break;

  case FM3TR_Constants::DATA_250_HPS:
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformPacketizer: Data mode, 250 hops/s." << std::endl;
#endif

    memcpy (output_ptr,    m_inv_s_code,     32);
    memcpy (output_ptr+32, m_inv_s_code, 32);
    memcpy (output_ptr+64, m_s_code, 32);
    output_ptr += 96;
    break;

  case FM3TR_Constants::VOICE_250_HPS:
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformPacketizer: Voice mode, 250 hops/s." << std::endl;
#endif

    memcpy (output_ptr,    m_inv_s_code, 32);
    memcpy (output_ptr+32, m_inv_s_code, 32);
    memcpy (output_ptr+64, m_inv_s_code, 32);
    output_ptr += 96;
    break;

  default:
#ifdef MCS_VERBOSE
    std::cout << "Undefined mode: " << mode << endl;
#endif
    break;
  }

  /*
   * Code words 3-6; send S'.
   */

  for (int i=0; i<4; i++) {
    memcpy (output_ptr, m_inv_s_code, 32);
    output_ptr += 32;
  }

  // Guard time bits.
  memset (output_ptr, 1, FM3TR_Constants::Preamble_Guard_Time_Bits);
  output_ptr += FM3TR_Constants::Preamble_Guard_Time_Bits;

  // Entire preamble should be ready; send it downstream now.
  m_fm3trout->pushData (m_output_preamble, 8*32 + FM3TR_Constants::Preamble_Guard_Time_Bits);
}

void
FM3TR_WaveformPacketizer::EncodeFrame (bool eom)
{
  char *input_ptr, *output_ptr;
  int i, j;

  char * temp_buffer = new char [320];

  if (!m_voice_mode) {
    /*
     * Data mode.
     * Unpack data and store into temporary buffer.
     */

    input_ptr = temp_buffer;

    // Unpack 320 bits from 64 bytes @ 5 bits.
    char sample;
    for (i=0; i<64; i++) {
      sample = m_input[m_num_read+i];

      *input_ptr++ = ((sample & 0x10) == 0x10) ? 1 : -1;
      *input_ptr++ = ((sample & 0x08) == 0x08) ? 1 : -1;
      *input_ptr++ = ((sample & 0x04) == 0x04) ? 1 : -1;
      *input_ptr++ = ((sample & 0x02) == 0x02) ? 1 : -1;
      *input_ptr++ = ((sample & 0x01) == 0x01) ? 1 : -1;
    }

    input_ptr = temp_buffer;
    m_input_count -= 64;
    m_num_read += 64;
  }
  else {
    // Voice mode.
    input_ptr = m_input;
    m_input_count -=320;
    m_num_read += 320;
  }

  output_ptr = m_output_frame;

  /*
   * Data hops.
   */

  for (i=0; i<FM3TR_Constants::Hops_Per_Frame; i++) {
    // Initial rise time.
    memset (output_ptr, 1, FM3TR_Constants::Nrt);
    output_ptr += FM3TR_Constants::Nrt;

    for (j=0; j<FM3TR_Constants::Ninf; j++) {
      // Information bits.
      *output_ptr++ = (*input_ptr++ > 0) ? 1 : -1;
    }

    // Fall time & transition time.
    memset (output_ptr, 1, FM3TR_Constants::Nrt+FM3TR_Constants::Ntt);
    output_ptr += FM3TR_Constants::Nrt+FM3TR_Constants::Ntt;
  }

  delete temp_buffer;

  /*
   * Synchronization hop.
   */

  // Rise time.
  memset (output_ptr, 1, FM3TR_Constants::Nrt);
  output_ptr += FM3TR_Constants::Nrt;

  // Guard time.
  for (j=0; j<FM3TR_Constants::Ngt; j++) {
    *output_ptr++ = (j&1) ? 1 : -1;
  }

  // If at end of message, send special sequence.
  if (eom) {
    memcpy (output_ptr, m_inv_s_code, 32);
    memcpy (output_ptr+32, m_inv_s_code, 32);
  }
  else {
    memcpy (output_ptr, m_s_code, 32);
    memcpy (output_ptr+32, m_s_code, 32);
  }

  output_ptr += 64;

  // Guard time.
  for (j=0; j<FM3TR_Constants::Ngt; j++) {
    *output_ptr++ = (j&1) ? 1 : -1;
  }

  // Fall time & transition time.
  memset (output_ptr, 1, FM3TR_Constants::Nrt+FM3TR_Constants::Ntt);
  output_ptr += FM3TR_Constants::Nrt+FM3TR_Constants::Ntt;
}

void
FM3TR_WaveformPacketizer::startStream ()
  throw (CORBA::SystemException)
{
  m_llc_enable = true;
}

void
FM3TR_WaveformPacketizer::stopStream ()
  throw (CORBA::SystemException)
{
  m_llc_enable = false;
}

/*
 * ----------------------------------------------------------------------
 * FM3TR Receiver
 * ----------------------------------------------------------------------
 */

status
FM3TR_WaveformReceiver::Create (FM3TR_WaveformReceiver *& res,
				const char * identifier)
{
  res = new FM3TR_WaveformReceiver (identifier);

  if (!res) {
    return fail;
  }

  return success;
}

FM3TR_WaveformReceiver::FM3TR_WaveformReceiver (const char * identifier)
  : CF__Resource_impl (identifier),
    m_input (0)
{
  m_input_count = 0;
  m_output = new char [FM3TR_Constants::Bits_Per_Frame];
  m_input_buffer_size = 1024;

  // Define data ports.
  m_fm3trin = new Fm3trInPort_impl (m_input_buffer_size);
  m_cvsdout = new CvsdOutPort_impl (m_output, FM3TR_Constants::Bits_Per_Frame);

  // Define MAC interfaces.
  m_mac_fc_out = 
    new MAC_Ctrl_OutPort_impl<SDR_FM3TR::MAC_Signaling::MAC_FlowControl_Local> ();

  // Initialize variables.
  m_threadrunning = false;
}

FM3TR_WaveformReceiver::~FM3TR_WaveformReceiver ()
{
  // destructor
  delete m_output;
  delete m_fm3trin;
  delete m_cvsdout;
  delete m_mac_fc_out;
}

CORBA::Object_ptr
FM3TR_WaveformReceiver::getPort (const CORBA::Char* name)
  throw (CORBA::SystemException,
	 CF::PortSupplier::UnknownPort)
{
  string temp(name);

  if (temp == "FM3TR_WaveformReceiverIn") {
    return m_fm3trin->getPort ();
  }
  else if (temp == "FM3TR_WaveformReceiverOut") {
    return m_cvsdout->getPort ();
  }
  else if (temp == "MAC_FlowCtrl_Out") {
    return m_mac_fc_out->getPort ();
  }
  else {
    throw CF::PortSupplier::UnknownPort();
  }
} // End of FM3TR_WaveformReceiver::getPort(const CORBA::Char* name)

void
FM3TR_WaveformReceiver::start ()
  throw (CORBA::SystemException,
	 CF::Resource::StartError)
{
  if (m_threadrunning) {
    return;
  }

  m_threadrunning = true;
  m_thread.start (RunWorker, (void *) this);
  CF__Resource_impl::start();
} // End of void FM3TR_WaveformReceiver::start ()

void
FM3TR_WaveformReceiver::stop ()
  throw (CORBA::SystemException,
	 CF::Resource::StopError)
{
  m_bStop = true;
  m_threadrunning = false;
  m_thread.detach ();
  m_fm3trin->ReleaseDataBuffer ();
} // End of void FM3TR_WaveformReceiver::stop ()

// Generic thread OS signature which calls component specific thread function.
void
FM3TR_WaveformReceiver::RunWorker (void * param)
{
  FM3TR_WaveformReceiver * component =
    (FM3TR_WaveformReceiver *) param;

#ifdef MCS_VERBOSE
  std::cout << "FM3TR_WaveformReceiver: Component is running." << std::endl;
#endif

  component->RunLoop();

#ifdef MCS_VERBOSE
  std::cout << "FM3TR_WaveformReceiver: Component is stopping." << std::endl;
#endif
} // End of FM3TR_WaveformReceiver::RunWorker ()

// Inner loop function that checks for a stop command
status
FM3TR_WaveformReceiver::RunLoop ()
{
  m_bStop = false;

  /*
   * For demonstration purposes, we will drive our output high/low 
   * watermark from a property.
   */

  bool check_for_HighWatermark = true;

  unsigned short new_hwm_prop_value, old_hwm_prop_value=0;

  /*
   * Check to see if we have such a high/low watermark property
   * Property name is "HighWaterMarkSet".
   */

  int high_water_mark_property_index = 
    GetPropertyIndex ("DCE:d5314efc-92ad-47d1-b001-5e07a35303b0");

  if (high_water_mark_property_index < 0) {
    check_for_HighWatermark = false;
  }

  while (!m_bStop) {
    // Check if we should send high/low water mark signals.
    if (check_for_HighWatermark) {
      // Get dynamic value of high water mark property.
      int status = GetPropertyValue (high_water_mark_property_index,
				     &new_hwm_prop_value);
      assert (0 == status);

      /*
       * Need to invoke the appropriate output interface if the property
       * has changed.
       */

      if (new_hwm_prop_value != old_hwm_prop_value) {
	// First check to see the output interface is connected
	SDR_FM3TR::MAC_Signaling::MAC_FlowControl_Local_ptr remote_fc_port;
	remote_fc_port = m_mac_fc_out->getRemotePort();

	if (m_mac_fc_out->IsConnected()) {
	  if (new_hwm_prop_value > 0) {
	    remote_fc_port->signalHighWatermark ();
	  }
	  else {
	    remote_fc_port->signalLowWatermark ();
	  }
	}

	old_hwm_prop_value = new_hwm_prop_value;
      }
    }

    Run ();
  }

  return success;
}

// Inner loop method that performs component business logic.
status
FM3TR_WaveformReceiver::Run ()
{
  int hop, frame;
  bool eom;
  unsigned long pattern;

  /*
   * We don't care about EOD coming from demodulator.
   */

  /*
   * Look for the preamble.
   */

  m_input_count = m_fm3trin->GetData (&m_input);

  if (m_input_count == 0) {
    return success;
  }

  m_samples_read = 0;

  // Data ready.  Try to acquire A-Code from preamble.
  if (!Acquire (m_operational_mode, pattern)) {
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Could not acquire Waveform; "
	      << "waiting and continuing." << std::endl;
#endif
    m_fm3trin->ReleaseDataBuffer (m_samples_read);
    return success;
  }

#ifdef MCS_VERBOSE
  std::cout << "WaveformReceiver: Waveform Acquired: ";

  switch (m_operational_mode) {
  case FM3TR_Constants::DATA_2000_HPS:  std::cout << "Data @ 2000 h/s"  << std::endl; m_voice_mode=false; break;
  case FM3TR_Constants::VOICE_2000_HPS: std::cout << "Voice @ 2000 h/s" << std::endl; m_voice_mode=true;  break;
  case FM3TR_Constants::DATA_1000_HPS:  std::cout << "Data @ 1000 h/s"  << std::endl; m_voice_mode=false; break;
  case FM3TR_Constants::VOICE_1000_HPS: std::cout << "Voice @ 1000 h/s" << std::endl; m_voice_mode=true;  break;
  case FM3TR_Constants::DATA_500_HPS:   std::cout << "Data @ 500 h/s"   << std::endl; m_voice_mode=false; break;
  case FM3TR_Constants::VOICE_500_HPS:  std::cout << "Voice @ 500 h/s"  << std::endl; m_voice_mode=true;  break;
  case FM3TR_Constants::DATA_250_HPS:   std::cout << "Data @ 250 h/s"   << std::endl; m_voice_mode=false; break;
  case FM3TR_Constants::VOICE_250_HPS:  std::cout << "Voice @ 250 h/s"  << std::endl; m_voice_mode=true;  break;
  default:                              std::cout << "unrecognized waveform" << std::endl; break;
  }

  std::cout << std::endl;
#endif

  m_fm3trin->ReleaseDataBuffer (m_samples_read);

  eom = false;
  frame = 0;

  while (!eom && !m_bStop) {
    /*
     * Extract data.
     */

    m_output_count = 0;

    m_input_count = m_fm3trin->GetData (&m_input);

    if (m_input_count == 0) {
      continue;
    }

    m_samples_read = 0;

    /*
     * Hop sequence should have exactly 500 samples; 100 from each hop:
     *   Hop 1 (data): Nrt(5) + Ninf(80) + Nrt(5) + Ntt(10)
     *   Hop 2 (data): same
     *   Hop 3 (data): same
     *   Hop 4 (data): same
     *   Hop 5 (sync): Nrt(5) + Ngt(8) + Ns(64) + Ngt(8) + Nrt(5) + Ntt(10)
     */

    for (hop=0; hop<FM3TR_Constants::Hops_Per_Frame; hop++) {
      if (!ExtractHop ()) { // read 100 samples
	/*
	 * Consider using "continue" instead of "break" to deal with data
	 * synch issues with demodulator; often an issue with reading more
	 * data.
	 */
#ifdef MCS_VERBOSE
	std::cout << "FM3TR_WaveformReceiver: Hop extraction failed." << std::endl;
#endif
	break;
      }
    }

    if (hop != FM3TR_Constants::Hops_Per_Frame) {
#ifdef MCS_VERBOSE
      std::cout << "FM3TR_WaveformReceiver: Frame extraction failed." << std::endl;
#endif
      m_fm3trin->ReleaseDataBuffer (m_samples_read);
      break;
    }

#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Extracting: frame "
	      << frame << ", " << m_output_count << " bits."
	      << endl;
#endif

    /*
     * Send extracted data.
     */

    frame++;
    m_cvsdout->pushData (m_output, m_output_count);

    /*
     * If extraction was fine, look for sync pattern.
     */

    if (!Sync (eom)) { // read 100 samples
#ifdef MCS_VERBOSE
      std::cout << "FM3TR_WaveformReceiver: Could not sync." << std::endl;
#endif
      m_fm3trin->ReleaseDataBuffer (m_samples_read);
      break;
    }

    m_fm3trin->ReleaseDataBuffer (m_samples_read);
  } // while (!eom) {

#ifdef MCS_VERBOSE
  if (eom) {
    std::cout << "FM3TR_WaveformReceiver: At EOM after "
	      << frame << " frames." << endl;
  }
  else {
    std::cout << "FM3TR_WaveformReceiver: Lost sync after "
	      << frame << " frames." << endl;
  }
#endif

  return success;
}  // End FM3TR_WaveformReceiver::Run()

bool
FM3TR_WaveformReceiver::Acquire (int & mode, unsigned long & pattern)
{
  /*
   * Tries to acquire waveform (correlates by looking for A-code).
   * Returns operational mode (hop rate, data/voice) if acquisition
   * is successful.
   */

  bool positive;
  int i;

  if (!InitCorrelator (pattern)) {
    return false;
  }

  /*
   * Wait for A-Code.
   */

  if (!Correlate (pattern, 
		  FM3TR_Constants::A_Code, THRESHOLD,
		  positive)) {
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Acquisition: Error waiting for A-Code."
	      << endl;
#endif
    return false;
  }

  /*
   * Found A-code.  Read 3 significant code words.
   */

  mode = 0;

  for (i=0; i<3; i++) {
    if (!InitCorrelator (pattern, SKIP_MATCH)) {
#ifdef MCS_VERBOSE
      std::cout << "FM3TR_WaveformReceiver: Acquisition: Error skipping to code word "
		<< i << endl;
#endif
      return false;
    }

    if (!Correlate (pattern,
		    FM3TR_Constants::S_Code, THRESHOLD,
		    positive, INTRA_PREAMBLE_TIMEOUT, 32-SKIP_MATCH)) {
#ifdef MCS_VERBOSE
      std::cout << "FM3TR_WaveformReceiver: Acquisition: Error correlating code word "
		<< i << endl;
#endif
      return false;
    }

    mode <<= 1;
    mode |= positive ? 0 : 1;
  }

  /*
   * Skip the remaining 4 code words.
   */

  if (!EatBits (4*32)) {
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Acquisition: Premature end of preamble."
	      << endl;
#endif
    return false;
  }

  /*
   * Skip preamble guard time.
   */

  if (!EatBits (FM3TR_Constants::Preamble_Guard_Time_Bits)) {
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Acquisition: Premature end of preamble."
	      << endl;
#endif
    return false;
  }

  return true;
}

bool
FM3TR_WaveformReceiver::ExtractHop ()
{
  /*
   * Ignore bits during rise time
   */

  if (!EatBits (FM3TR_Constants::Nrt)) {
    return false;
  }

  /*
   * Extract information bits.
   */

  if (!ReadMore (FM3TR_Constants::Ninf)) {
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Hop extration: Could not read " 
	      << FM3TR_Constants::Ninf << " bits." << std::endl;
#endif
    return false;
  }

  int i, j;
  if (!m_voice_mode) {
    /*
     * Data mode; pack output data and store into temporary buffer.
     */

    char sample;
    for (i=0; i<16; i++) {
      sample = 0x00;
      for (j=0; j<5; j++) {
	sample <<= 1;
	sample |= (*m_input++ > 0) ? 1 : 0;
      }
      m_output[m_output_count+i] = sample;
    }

    m_output_count += 16;
  }
  else {
    /*
     * Voice mode.
     */

    for (i=0; i<FM3TR_Constants::Ninf; i++) {
      m_output[m_output_count+i] = (*m_input++ > 0) ? 1 : 0;
    }

    m_output_count += FM3TR_Constants::Ninf;
  }

  m_input_count -= FM3TR_Constants::Ninf;
  m_samples_read += FM3TR_Constants::Ninf;

  /*
   * Ignore bits during fall and transition times.
   */

  if (!EatBits (FM3TR_Constants::Nrt+FM3TR_Constants::Ntt)) {
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Hop extraction: could not skip " 
	      << FM3TR_Constants::Nrt+FM3TR_Constants::Ntt << " bits."
	      << std::endl;
#endif
    return false;
  }

  return true;
}

bool
FM3TR_WaveformReceiver::Sync (bool & eom)
{
  unsigned long pattern;
  bool positive1, positive2;

#ifdef MCS_VERBOSE
  std::cout << "FM3TR_WaveformReceiver: Synchronization: "
	    << "Trying to synchronize."
	    << endl;
#endif

  if (!InitCorrelator (pattern)) {
    return false;
  }

  /*
   * Wait for S-Code, twice. Negative match indicates EOM
   */

  if (!Correlate (pattern,
		  FM3TR_Constants::S_Code, THRESHOLD,
		  positive1,
		  SYNC_TIMEOUT,
		  FM3TR_Constants::Nrt+FM3TR_Constants::Ngt)) {
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Synchronization: "
	      << "Error waiting for S-Code."
	      << endl;
#endif
    return false;
  }

  if (!InitCorrelator (pattern, SKIP_MATCH)) {
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Synchronization: "
	      << "Error skipping to second code word."
	      << endl;
#endif
    return false;
  }

  if (!Correlate (pattern,
		  FM3TR_Constants::S_Code, THRESHOLD,
		  positive2,
		  SYNC_TIMEOUT,
		  32-SKIP_MATCH)) {
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Synchronization: "
	      << "Error correlating second code word."
	      << endl;
#endif
    return false;
  }

  eom = !positive1 || !positive2;

  if ((positive1 && !positive2) ||
      (!positive1 && positive2)) {
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Synchronization: "
	      << "Conflicting signals about EOM, assuming EOM."
	      << endl;
#endif
  }

  if (!EatBits (FM3TR_Constants::Ngt +
		FM3TR_Constants::Nrt +
		FM3TR_Constants::Ntt)) {
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Synchronization: "
	      << "Error eating rest of sync."
	      << endl;
#endif
    return false;
  }

#ifdef MCS_VERBOSE
  std::cout << "FM3TR_WaveformReceiver: Synchronization: "
	    << "Synchronized, " << (eom ? "at EOM" : "not at EOM")
	    << endl;
#endif

  return true;
}

/*
 * Initialize the correlator by reading bits into a pattern.
 */

bool
FM3TR_WaveformReceiver::InitCorrelator (unsigned long & pattern,
                                        int shift)
{
  // default shift is 32

  if (!ReadMore (shift)) {
#ifdef MCS_VERBOSE
    std::cout << "FM3TR_WaveformReceiver: Correlator: Could not read "
	      << shift << " bits." << endl;
#endif
    return false;
  }

  for (int i=0; i < shift; i++) {
    pattern <<= 1;
    pattern  |= (*m_input++ > 0) ? 1 : 0;
    m_input_count--;
    m_samples_read++;
  }

  return true;
}

/*
 * Look for an auto-correlation code; try for at max max_skip bits,
 * and complain if we have to shift more or less than expected_shift
 * bits (if a pattern is expected in a specific location, this means
 * that some bits were lost or extraneous).
 */

bool
FM3TR_WaveformReceiver::Correlate (unsigned long & pattern,
                                   unsigned long code,
				   int threshold,
                                   bool & positive,
                                   int max_skip,
                                   int expected_shift)
{
  /*
   * If max_skip is set to -1, correlator will only exit when code is found.
   */

  int bitcount;
  int counter=0;

  while (true) {
    /*
     * Continue looping until
     *   1. no more data is available,
     *   2. synchronization timeout occurs (max_skip), or
     *   3. synchronization is successful (correlation > threshold).
     */

    bitcount = CountBits (pattern ^ code); // count dissimilar bits

    if (bitcount >= threshold) {
      positive = false;
      break;
    }
    else if (bitcount <= -threshold) {
      positive = true;
      break;
    }

    /*
     * Stop looking after trying for too long.
     */

    if (max_skip != -1 && counter >= max_skip) {
#ifdef MCS_VERBOSE
      std::cout << "FM3TR_WaveformReceiver: Correlator: "
		<< "Timed out looking for 0x"
		<< std::hex << code << std::dec
		<< " after " << counter << " bits"
		<< endl;
#endif
      return false;
    }

    /*
     * If input buffer is empty, read some more.
     */

    if (m_input_count==0) {
      if (!ReadMore ()) {
#ifdef MCS_VERBOSE
	std::cout << "FM3TR_WaveformReceiver: Correlator: "
		  << "Could not read more bits." << endl;
#endif
	return false;
      }
    }

    /*
     * Shift and read new symbol
     */

    pattern <<= 1;
    pattern  |= (*m_input++ > 0) ? 1 : 0;

    m_input_count--;
    m_samples_read++;
    counter++;
  } // while

#ifdef MCS_VERBOSE
  std::cout << "FM3TR_WaveformReceiver: Correlator: "
	    << "Found 0x"
	    << std::hex << code << std::dec
	    << ", skipped " << counter << " bits"
	    << " (" << (positive ? "positive" : "negative") << ")"
	    << "."
	    << endl;
#endif

#ifdef MCS_VERBOSE
  if (expected_shift != -1 && counter != expected_shift) {
    std::cout << "FM3TR_WaveformReceiver: Correlator: "
	      << "Out of sync, shift was "
	      << counter << ", expected " << expected_shift
	      << "."
	      << endl;
  }
#endif

  return true;
} // FM3TR_WaveformReceiver::Correlate()

/*
 * ReadMore makes sure that at least the specified amount of bits
 * is available. If there are less, try to read more from the source.
 */

bool
FM3TR_WaveformReceiver::ReadMore (int count) // initially, count=1
{
  if (m_input_count >= count) {
    return true;
  }

  // Release what's been read and try to get some more data
  m_fm3trin->ReleaseDataBuffer (m_samples_read);
  m_input_count = m_fm3trin->GetData (&m_input);
  m_samples_read = 0;

  int timeout = 0;
  while (m_input_count < count) {
    timeout++;

    if (timeout > 10) {
      // give it up
      return false;
    }
    else {
      // release buffer and try to read more data
      m_fm3trin->ReleaseDataBuffer (0);
      m_input_count = m_fm3trin->GetData (&m_input);
    }
  } // while

  return true;
} // FM3TR_WaveformReceiver::ReadMore()

/*
 * EatBits just ignores some bits in the input data.
 */

bool
FM3TR_WaveformReceiver::EatBits (int count)
{
  if (!ReadMore (count)) {
    return false;
  }

  m_input += count;
  m_input_count -= count;
  m_samples_read += count;
  return true;
}
